﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CPPEI.Update.WCF
{
    /// <summary>
    /// 上传进行中的回调
    /// </summary>
    /// <param name="fileName"></param>
    /// <param name="start"></param>
    /// <param name="length"></param>
    /// <param name="totalSize"></param>
    /// <param name="netSpeed">kb/s</param>
    public delegate void OnUploadingCallback(string fileName, long start, int length, long totalSize, double netSpeed);

    /// <summary>
    /// 上传出错的回调
    /// </summary>
    /// <param name="errorMsg"></param>
    /// <param name="start"></param>
    /// <param name="length"></param>
    /// <param name="totalSize"></param>
    public delegate void OnUploadingFailureCallback(string errorMsg, long start, int length, long totalSize);

    /// <summary>
    /// 上传线程
    /// </summary>
    public class UploadThread
    {
        private WCFServiceRef.UpdateServiceClient _updateServiceClient;

        /// <summary>
        /// 上传进度事件
        /// </summary>
        public event OnUploadingCallback Progress;

        /// <summary>
        /// 上传失败事件
        /// </summary>
        public event OnUploadingFailureCallback Failure;

        Thread _uploadThread;

        public bool _isRunning = false;

        private string _product;
        private string _appType;
        private string _fromVersion;
        private string _toVersion;
        private string _pathFilePath;

        /// <summary>
        /// 一个包的数据长度
        /// </summary>
        private const int SegmentLength = 4096;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public UploadThread(string ip, int port)
        {
            _updateServiceClient = CreateUpdateServiceClient(ip, port);
        }

        /// <summary>
        /// 创建访问更新服务器的客户端对象
        /// </summary>
        /// <param name="hostConfig"></param>
        /// <returns></returns>
        private static WCFServiceRef.UpdateServiceClient CreateUpdateServiceClient(string ip, int port)
        {
            System.ServiceModel.Channels.Binding tcpBinding = new NetTcpBinding();
            (tcpBinding as NetTcpBinding).Security.Mode = SecurityMode.None;
            (tcpBinding as NetTcpBinding).MaxBufferSize = 1024 * 1024 * 1024;
            (tcpBinding as NetTcpBinding).MaxReceivedMessageSize = 1024 * 1024 * 1024;
            EndpointAddress address = new EndpointAddress(string.Format("net.tcp://{0}:{1}/{2}/", ip, port, "UpdateService"));

            WCFServiceRef.UpdateServiceClient updateServiceClient = new WCFServiceRef.UpdateServiceClient(tcpBinding, address);
            return updateServiceClient;
        }

        /// <summary>
        /// 上传增量式更新包文件
        /// </summary>
        /// <param name="product"></param>
        /// <param name="appType"></param>
        /// <param name="fromVersion"></param>
        /// <param name="toVersion"></param>
        /// <param name="patchFilePath"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public void UploadIncrementalPatch(string product, 
            string appType, 
            string fromVersion, 
            string toVersion, 
            string patchFilePath)
        {
            if (_isRunning)
            {
                throw new Exception(string.Format("正在上传文件中,file:{0}", _pathFilePath));
            }

            if(!File.Exists(patchFilePath))
            {
                throw new Exception(string.Format("文件不存在,file:{0}", patchFilePath));
            }

            _uploadThread = new Thread(RunUploadIncrementalPatch);

            _product = product;
            _appType = appType;
            _fromVersion = fromVersion;
            _toVersion = toVersion;
            _pathFilePath = patchFilePath;

            _isRunning = true;
            _uploadThread.Start();
        }

        private void RunUploadIncrementalPatch()
        {
            byte[] data = new byte[SegmentLength];
            long startIndex = 0;
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();

            using (FileStream fileStream = new FileStream(_pathFilePath, FileMode.Open))
            {
                int len = fileStream.Read(data, 0, SegmentLength);
                long totalSize = fileStream.Length;
                fileStream.Seek(0, SeekOrigin.Begin);
                while (len > 0)
                {
                    WCFServiceRef.UploadResult result = null;
                    try
                    {
                        watch.Restart();
                        result = _updateServiceClient.UploadIncrementalPatchFileSegment(_product,
                            _appType,
                            _fromVersion,
                            _toVersion,
                            startIndex,
                            len,
                            data);
                        watch.Stop();
                        double speed = len / watch.ElapsedMilliseconds / 1000 / 1000;
                        if (Progress != null)
                        {
                            Progress(result.FileName, startIndex, len, totalSize, speed);
                        }
                    }
                    catch(Exception ex)
                    {
                        if (Failure != null)
                        {
                            Failure(ex.Message, startIndex, len, totalSize);
                        }
                    }

                    if (result != null)
                    {
                        startIndex += len;
                        len = fileStream.Read(data, 0, SegmentLength);
                    }
                }
            }
            _isRunning = false;
        }

        /// <summary>
        /// 上传完整包
        /// </summary>
        /// <param name="product"></param>
        /// <param name="appType"></param>
        /// <param name="toVersion"></param>
        /// <param name="patchFilePath"></param>
        public void UploadFullPatch(string product,
            string appType,
            string toVersion,
            string patchFilePath)
        {
            if (_isRunning)
            {
                throw new Exception(string.Format("正在上传文件中,file:{0}", _pathFilePath));
            }

            if (!File.Exists(patchFilePath))
            {
                throw new Exception(string.Format("文件不存在,file:{0}", patchFilePath));
            }

             _uploadThread = new Thread(RunUploadFullPatch);

            _product = product;
            _appType = appType;
            _toVersion = toVersion;
            _pathFilePath = patchFilePath;

            _isRunning = true;
            _uploadThread.Start();
        }

        private void RunUploadFullPatch()
        {
            byte[] data = new byte[SegmentLength];
            long startIndex = 0;
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();

            using (FileStream fileStream = new FileStream(_pathFilePath, FileMode.Open))
            {
                int len = fileStream.Read(data, 0, SegmentLength);
                long totalSize = fileStream.Length;
                fileStream.Seek(0, SeekOrigin.Begin);
                while (len > 0)
                {
                    WCFServiceRef.UploadResult result = null;
                    try
                    {
                        watch.Restart();
                        result = _updateServiceClient.UploadFullPatchFileSegment(_product,
                            _appType,
                            _toVersion,
                            startIndex,
                            len,
                            data);
                        watch.Stop();
                        double speed = len / watch.ElapsedMilliseconds / 1000 / 1000;
                        if (Progress != null)
                        {
                            Progress(result.FileName, startIndex, len, totalSize, speed);
                        }
                    }
                    catch (Exception ex)
                    {
                        if (Failure != null)
                        {
                            Failure(ex.Message, startIndex, len, totalSize);
                        }
                    }

                    if (result != null)
                    {
                        startIndex += len;
                        len = fileStream.Read(data, 0, SegmentLength);
                    }
                }
            }
            _isRunning = false;
        }
    }
}
